package com.caece.ajedrez;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.caece.ajedrez.Excepciones.DestionoIncorrectoException;
import com.caece.ajedrez.Excepciones.FichaContrariaException;
import com.caece.ajedrez.Excepciones.FueraDeTableroException;
import com.caece.ajedrez.Excepciones.MovimientoException;
import com.caece.ajedrez.Excepciones.NoHayFichaException;
import com.caece.ajedrez.Fichas.Alfil;
import com.caece.ajedrez.Fichas.Ficha;
import com.caece.ajedrez.Fichas.Ficha.Color;
import com.caece.ajedrez.Fichas.Peon;
import com.caece.ajedrez.Fichas.Reina;
import com.caece.ajedrez.Fichas.Rey;
import com.caece.ajedrez.Fichas.Torre;
import com.caece.ajedrez.Movimientos.Movimiento;
import com.caece.ajedrez.Movimientos.Posicion;

public class Tablero {
	//tamaño del tablero
	public static final int SIZE = 8;
	//constante para el tiempo maximo de juego son 10 minutos expresados en milisegundos
	public static Long MAX_TIME = 10*60*1000L;
	
	private Jugador jugadorBlanco;
	private Jugador jugadorNegro;
	private Ficha[][] tablero;
	private Boolean juegaColor;
	private Boolean jaqueMate;
	private Jugador ganador;
	
	public Tablero(){
		tablero = new Ficha[Tablero.SIZE][Tablero.SIZE];
		crearTablero();
		
		jugadorBlanco = new Jugador(Color.Blanco, MAX_TIME);
		jugadorNegro = new Jugador(Color.Negro, MAX_TIME);
		
		juegaColor = false;
		jaqueMate = false;
	}

	//creo el tablero
	private void crearTablero() {
		for(int i = 0; i<Tablero.SIZE; i++){
			for(int j = 0; j<Tablero.SIZE; j++){
				tablero[i][j] = null;
			}
		}
		
		crearPeones();
		crearTorres();
		crearAlfiles();
		crearReyes();
	}

	private void crearReyes() {
		tablero[0][4] = new Reina(Color.Blanco);
		tablero[0][3] = new Rey(Color.Blanco);
		tablero[7][3] = new Rey(Color.Negro);
		tablero[7][4] = new Reina(Color.Negro);
	}

	private void crearAlfiles() {
		tablero[0][2] = new Alfil(Color.Blanco);
		tablero[0][5] = new Alfil(Color.Blanco);
		tablero[7][2] = new Alfil(Color.Negro);
		tablero[7][5] = new Alfil(Color.Negro);
	}

	private void crearPeones() {
		for(int i = 0; i<Tablero.SIZE; i++){
			tablero[1][i] = new Peon(Color.Blanco);
			tablero[6][i] = new Peon(Color.Negro);
		}
		//saco los peones de los caballos
		tablero[1][1] = null;
		tablero[1][6] = null;
		tablero[6][1] = null;
		tablero[6][6] = null;
		
	}
	
	private void crearTorres() {
		tablero[0][0] = new Torre(Color.Blanco);
		tablero[0][7] = new Torre(Color.Blanco);
		tablero[7][7] = new Torre(Color.Negro);
		tablero[7][0] = new Torre(Color.Negro);
	}
	
	public void mover(Posicion desde, Posicion hasta) throws MovimientoException{
		if(desde.getX() > Tablero.SIZE || desde.getY() > Tablero.SIZE || hasta.getX() > Tablero.SIZE || hasta.getY() > Tablero.SIZE){
			throw new FueraDeTableroException();
		}
		
		Ficha fichaElegida = tablero[desde.getY()][desde.getX()];
		Boolean pudoMover = false;
		if(fichaElegida != null){
			//si trato de jugar con una ficha del color contrario
			if(!fichaElegida.getColor().equals(getColorJugando())){
				throw new FichaContrariaException();
			}
			List<Movimiento> mPosibles = fichaElegida.getMovimientos(desde);
			for(Movimiento m : mPosibles){
				if(m.getPosiciones().contains(hasta)){
					Boolean hayObstaculo = false;
					for(Posicion pos : m.getPosiciones()){
						if(pos.getX()>-1 && pos.getY()>-1 && pos.getX()<Tablero.SIZE && pos.getY()<Tablero.SIZE && !hayObstaculo){
							Ficha obstaculo  = tablero[pos.getY()][pos.getX()];

							if(pos.equals(hasta)){
								//me fijo si hay alguien en el destino y si no es del mismo color lo como
								if(obstaculo!=null && m.getAtaca(pos) && !fichaElegida.getColor().equals(obstaculo.getColor())){
									tablero[desde.getY()][desde.getX()] = null;
									tablero[hasta.getY()][hasta.getX()] = fichaElegida;
									fichaElegida.movio();
									pudoMover = true;
									if(obstaculo instanceof Rey){
										jaqueMate = true;
										ganador = getJugadorJugando();
									}
								}else if(obstaculo == null && ((m.getAtaca(pos) && !(fichaElegida instanceof Peon)) || (!m.getAtaca(pos) && fichaElegida instanceof Peon))){
									//no hubo obstaculos y pude mover
									if(pos.equals(hasta)){
										tablero[desde.getY()][desde.getX()] = null;
										tablero[hasta.getY()][hasta.getX()] = fichaElegida;
										fichaElegida.movio();
										pudoMover = true;
									}
								}
							}else{
								if(obstaculo!=null){
									hayObstaculo = true;
								}
							}
						}
					}
				}
			}
			
			if(!pudoMover){
				throw new DestionoIncorrectoException();
			}else{
				getJugadorJugando().addMovimiento(desde, hasta);
				cambiarJugador();
			}
		}else{
			throw new NoHayFichaException();
		}
	}
	
	public void esperandoJugador(){
		getJugadorJugando().juega();
	}
	
	private void cambiarJugador(){
		getJugadorJugando().cambioTurno();
		juegaColor = !juegaColor;
	}
	
	public Color getColorJugando(){
		Color color = null;
		if(juegaColor){
			color = Color.Negro;
		}else{
			color = Color.Blanco;
		}
		
		return color;
	}
	
	public Ficha getFichaAt(Posicion pos){
		Ficha ret = null;
		
		ret = tablero[pos.getX()][pos.getY()];
		
		return ret;
	}
	
	public Jugador getJugadorBlanco(){
		return jugadorBlanco;
	}
	
	public Jugador getJugadorNegro(){
		return jugadorNegro;
	}
	
	public Jugador getJugadorJugando(){
		Jugador j = null;
		
		switch(getColorJugando()){
			case Negro:
				j = jugadorNegro;
				break;
			case Blanco:
				j = jugadorBlanco;
				break;
		}
		
		return j;
	}
	
	public boolean hayJaqueMate() {
		return jaqueMate;
	}
	
	public Jugador getGanador(){
		return ganador;
	}

	public boolean hayTiempo() {
		return !jugadorBlanco.isTimeUp() && !jugadorNegro.isTimeUp();
	}

	public boolean hayJaque() {
		Map<Posicion, Ficha> blancas = getFichasBlancas();
		Map<Posicion, Ficha> negras = getFichasNegras();
		Posicion posReyNegro = getReyNegro();
		Posicion posReyBlanco = getReyBlanco();
		Boolean jaque = false;
		
		//busco jaque negro
		for(Posicion pos : blancas.keySet()){
			Ficha ficha = blancas.get(pos);
			List<Movimiento> mPosibles = ficha.getMovimientos(pos);
			
			for(Movimiento mov : mPosibles){
				if(mov.getPosiciones().contains(posReyNegro)){
					Boolean hayObstaculo = false;
					for(Posicion pos2 : mov.getPosiciones()){
						if(pos2.getX()>-1 && pos2.getY()>-1 && pos2.getX()<Tablero.SIZE && pos2.getY()<Tablero.SIZE && !hayObstaculo){
							Ficha obstaculo  = tablero[pos2.getY()][pos2.getX()];
							if(obstaculo!=null && !(obstaculo instanceof Rey)){
								hayObstaculo = true;
							}else{
								if(obstaculo!=null && obstaculo instanceof Rey && !obstaculo.isBlanco()){
									jaque = true;
								}
							}
						}
					}
				}
			}
		}
		
		//busco jaque blanco. Si ya hay jaque no me importa porque solo voy a indicar si hay jaque en algun lado
		if(!jaque){
			for(Posicion pos : negras.keySet()){
				Ficha ficha = negras.get(pos);
				List<Movimiento> mPosibles = ficha.getMovimientos(pos);
				
				for(Movimiento mov : mPosibles){
					if(mov.getPosiciones().contains(posReyBlanco)){
						Boolean hayObstaculo = false;
						for(Posicion pos2 : mov.getPosiciones()){
							if(pos2.getX()>-1 && pos2.getY()>-1 && pos2.getX()<Tablero.SIZE && pos2.getY()<Tablero.SIZE && !hayObstaculo){
								Ficha obstaculo  = tablero[pos2.getY()][pos2.getX()];
								if(obstaculo!=null && !(obstaculo instanceof Rey)){
									hayObstaculo = true;
								}else{
									if(obstaculo!=null && obstaculo instanceof Rey && obstaculo.isBlanco()){
										jaque = true;
									}
								}
							}
						}
					}
				}
			}
		}
		
		return jaque;
	}

	private Posicion getReyBlanco() {
		return getRey(Color.Blanco);
	}

	private Posicion getReyNegro() {
		return getRey(Color.Negro);
	}

	private Posicion getRey(Color color) {
		Posicion pos = null;
		for(int i=0; i<Tablero.SIZE; i++){
			for(int j=0; j<Tablero.SIZE; j++){
				Ficha ficha = tablero[i][j];
				if(ficha != null && ficha instanceof Rey && ficha.getColor().equals(color)){
					pos = new Posicion(j, i);
				}
			}
		}
		
		return pos;
	}

	private Map<Posicion, Ficha> getFichasNegras() {
		return getFichas(Color.Negro);
	}

	private Map<Posicion, Ficha> getFichasBlancas() {
		return getFichas(Color.Blanco);
	}

	private Map<Posicion, Ficha> getFichas(Color color) {
		Map<Posicion, Ficha> fichas = new HashMap<Posicion, Ficha>();
		for(int i=0; i<Tablero.SIZE; i++){
			for(int j=0; j<Tablero.SIZE; j++){
				Ficha ficha = tablero[i][j];
				if(ficha != null && ficha.getColor().equals(color)){
					Posicion pos = new Posicion(j, i);
					fichas.put(pos, ficha);
				}
			}
		}
		
		return fichas;
	}
}
